﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using SilverTile.Xaml;
using System.Windows.Markup;
using SilverTile.Viewports;
using SilverTile.Sprites;
using SilverTile;

namespace SilverTileTests
{

    [TestClass]
    public class XamlSilverTileGameWorldTests
    {

        private string _xaml =
            @"
            <ResourceDictionary
                xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" 
                xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
                xmlns:st=""clr-namespace:SilverTile.Xaml;assembly=SilverTile"">

                <st:SpriteFrame x:Key=""Sand"" Source=""/SilverTileTests;component/Tiles/0002.png"" />    

                <st:GameAreaGrid x:Key=""Area1"" AreaName=""Area1"" Columns=""2"" Rows=""2"" TileWidth=""32"" TileHeight=""32"">
                    <st:SpriteTile X=""0"" Y=""0"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""1"" Y=""0"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""0"" Y=""1"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""1"" Y=""1"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                </st:GameAreaGrid>

                <st:GameAreaGrid x:Key=""Area2"" AreaName=""Area2"" Columns=""2"" Rows=""2"" TileWidth=""32"" TileHeight=""32"">
                    <st:SpriteTile X=""0"" Y=""0"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""1"" Y=""0"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""0"" Y=""1"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                    <st:SpriteTile X=""1"" Y=""1"" Blocking=""False"" Frame=""{StaticResource Sand}"" />
                </st:GameAreaGrid>

                <st:SilverTileGameWorld x:Key=""GameWorld"" StartAreaName=""Area1"" StartRow=""0"" StartColumn=""1"">
                    <st:SilverTileGameWorld.GameAreas>
                        <StaticResource ResourceKey=""Area1"" />
                        <StaticResource ResourceKey=""Area2"" />
                    </st:SilverTileGameWorld.GameAreas>
                </st:SilverTileGameWorld>
            </ResourceDictionary>
            ";

        [TestMethod]
        public void SilverTileGameWorld_Should_Be_IUpdateable()
        {
            var gameWorld = new SilverTileGameWorld();
            Assert.IsInstanceOfType(gameWorld, typeof(IUpdateable), "SilverTileGameWorld is not IUpdateable");
        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Have_GameAreas_Property()
        {

            var gameWorld = new SilverTileGameWorld();

            Assert.IsInstanceOfType(gameWorld.GameAreas, typeof(List<GameAreaGrid>), 
                "GameAreas property is not a dictionary of GameAreaGrid objects");

        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Be_Creatable_From_Xaml()
        {

            ResourceDictionary dict = (ResourceDictionary)XamlReader.Load(_xaml);
            object gameWorld =  dict["GameWorld"] as SilverTileGameWorld;

            Assert.IsNotNull(gameWorld, "GameWorld is null");
            Assert.IsInstanceOfType(gameWorld, typeof(SilverTileGameWorld), "GameWorld is not a SilverTileGameWorld");

        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Be_Set_Up_Correctly_From_Xaml()
        {

            ResourceDictionary dict = (ResourceDictionary)XamlReader.Load(_xaml);
            SilverTileGameWorld gameWorld = dict["GameWorld"] as SilverTileGameWorld;

            Assert.AreEqual(2, gameWorld.GameAreas.Count, "Incorrect number of GameAreas read");
            Assert.AreEqual("Area1", gameWorld.StartAreaName, "Incorrect start area");
            Assert.AreEqual(0, gameWorld.StartRow, "Incorrect start row");
            Assert.AreEqual(1, gameWorld.StartColumn, "Incorrect start column");

        }

        [TestMethod]
        public void SilverTileGameWorld_GetGameArea_Should_Return_GameArea_With_The_Give_Name()
        {

            ResourceDictionary dict = (ResourceDictionary)XamlReader.Load(_xaml);
            SilverTileGameWorld gameWorld = dict["GameWorld"] as SilverTileGameWorld;

            GameAreaGrid area2 = gameWorld.GetGameArea("Area2");
            Assert.IsNotNull(area2, "Returned GameAreaGrid is null");

            GameAreaGrid missingArea = gameWorld.GetGameArea("NotThere");
            Assert.IsNull(missingArea, "Missing area should return null");
        
        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Have_A_Viewport_Property()
        {

            var gameWorld = new SilverTileGameWorld();
            var viewport = new Viewport(10,10);

            gameWorld.Viewport = viewport;

            Assert.AreSame(viewport, gameWorld.Viewport, "Viewport was not set correctly");

        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Have_A_StartAreaName_Property()
        {

            var gameWorld = new SilverTileGameWorld();
            gameWorld.StartAreaName = "StartArea";

            Assert.AreEqual("StartArea", gameWorld.StartAreaName, "StartAreaName was not set properly");
            Assert.AreEqual(gameWorld.StartAreaName, gameWorld.GetValue(SilverTileGameWorld.StartAreaNameProperty), 
                "StartAreaName and StartAreaNameProperty does not reutrn the same value");

        }

        [TestMethod]
        public void SilverTileGameWorld_Should_Be_Aware_Of_A_Player_Controlled_Sprite()
        {

            var gameWorld = new SilverTileGameWorld();
            var playerSprite = new Sprite(10, 10);
            var viewport = new Viewport(20, 20);
            gameWorld.Viewport = viewport;

            viewport.AddSprite("PlayerSprite", playerSprite);

            gameWorld.PlayerSprite = playerSprite;

            Assert.AreSame(playerSprite, gameWorld.PlayerSprite, "Player sprite was not set correctly");
            
        }

        [TestMethod]
        public void Adding_Player_Sprite_To_GameWorld_Should_Add_It_To_Viewport()
        {

            var gameWorld = new SilverTileGameWorld();
            var playerSprite = new Sprite(10, 10);
            var viewport = new Viewport(20, 20);
            gameWorld.Viewport = viewport;

            gameWorld.PlayerSprite = playerSprite;

            Assert.AreSame(gameWorld.PlayerSprite, 
                gameWorld.Viewport.ActiveSprites[SilverTileGameWorld.PlayerSpriteKey], 
                "Player was not added to the viewport");         

        }
        
        [TestMethod]
        public void GameWorld_Should_Have_An_ActiveArea_Property()
        {

            var gameWorld = new SilverTileGameWorld();
            var area1 = new GameAreaGrid { AreaName = "Area1" };
            var area2 = new GameAreaGrid { AreaName = "Area2" };
            gameWorld.GameAreas.Add(area1);
            gameWorld.GameAreas.Add(area2);

            gameWorld.ActiveArea = area1;

            Assert.AreSame(area1, gameWorld.ActiveArea, "ActiveArea was not set correctly");
        
        }

        
        [TestMethod]
        public void GameWorld_And_Game_Should_Reflect_The_Same_ActiveArea()
        {

            var gameWorld = new SilverTileGameWorld();
            var area1 = new GameAreaGrid { AreaName = "Area1" };
            var area2 = new GameAreaGrid { AreaName = "Area2" };
            gameWorld.GameAreas.Add(area1);
            gameWorld.GameAreas.Add(area2);

            var game = new SilverTileGame();
            game.GameWorld = gameWorld;

            gameWorld.ActiveArea = area1;
            Assert.AreSame(gameWorld.ActiveArea, game.ActiveGameArea, "GameWorld and Game does not return the same active GameAreaGrid");

            game.ActiveGameArea = area2;
            Assert.AreSame(gameWorld.ActiveArea, game.ActiveGameArea, "GameWorld and Game does not return the same active GameAreaGrid");
            
        }

        [TestMethod]
        public void When_Player_Enters_Area_With_Transition_A_Transition_Should_Be_Performed()
        {

            var gameWorld = new SilverTileGameWorld();

            SpriteFrame frame = new SpriteFrame { Source = "/SilverTileTests;component/Tiles/0002.png" };

            GameAreaGrid area1 = new GameAreaGrid { AreaName = "Start", Rows = 2, Columns = 2, TileWidth = 20, TileHeight = 20 };
            area1.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 0, Frame = frame });
            area1.BackgroundSprites.Add(new SpriteTile { X = 1, Y = 0, Frame = frame });
            area1.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 1, Frame = frame });

            SilverTile.Xaml.AreaTransition transition = new SilverTile.Xaml.AreaTransition { GameAreaName = "End", TargetRow = 0, TargetColumn = 0 };
            SpriteTile transitionTile = new SpriteTile { X = 1, Y = 1, Transition = transition, Frame = frame };
            area1.BackgroundSprites.Add(transitionTile);

            GameAreaGrid area2 = new GameAreaGrid { AreaName = "End", Rows = 2, Columns = 2, TileWidth = 20, TileHeight = 20 };
            area2.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 0, Frame = frame });
            area2.BackgroundSprites.Add(new SpriteTile { X = 1, Y = 0, Frame = frame });
            area2.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 1, Frame = frame });
            area2.BackgroundSprites.Add(new SpriteTile { X = 1, Y = 1, Frame = frame });

            gameWorld.GameAreas.AddRange(new GameAreaGrid[] { area1, area2});

            gameWorld.StartAreaName = "Start";
            gameWorld.StartColumn = 0;
            gameWorld.StartRow = 0;

            SilverTileGame game = new SilverTileGame();
            game.Width = 100;
            game.Height = 100;
            game.GameWorld = gameWorld;



            var playerSprite = new Sprite(10, 10);
            gameWorld.PlayerSprite = playerSprite;


            Assert.AreEqual(area1, game.ActiveGameArea, "The start area is not area1");
            game.Update(TimeSpan.FromMilliseconds(200));

            playerSprite.X = 25;
            playerSprite.Y = 25;

            game.Update(TimeSpan.FromMilliseconds(200));

            Assert.AreEqual(area2, game.ActiveGameArea, "The area was not changed after the player entered the transition area");
        }

    }
}
